package croe.jdbc.dao;


import croe.jdbc.common.AppContextSupport;
import croe.jdbc.dao.impl.DefaultDataTypeConver;
import croe.jdbc.dao.impl.TransactionManager;
import croe.jdbc.domain.SQL;
import croe.jdbc.utils.ClassMethodUtil;
import croe.jdbc.utils.ResultSetUtils;
import croe.jdbc.utils.SqlUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * User: zengzl
 * Time: 2014-07-03 21:57
 */
@SuppressWarnings("unchecked")
public final class DaoOperations {
    private static JdbcTemplate jdbcTemplate;
    private static DataTypeConver dataTypeConver;

    private DaoOperations() {
    }

    static {
        jdbcTemplate = (JdbcTemplate) AppContextSupport.getAppContext().getBean("jdbcTemplate");
        try {
            dataTypeConver = (DataTypeConver) AppContextSupport.getAppContext().getBean("dateTypeConver");
        } catch (Exception ex) {
            if (dataTypeConver == null) {
                dataTypeConver = new DefaultDataTypeConver();
            }
        }
        if (jdbcTemplate == null) {
            throw new RuntimeException("请在Spring配置文件上配置jdbcTemplate");
        }
    }

    public static <T> int insert(T t) {
        try {
            SQL sql = SqlUtils.buildInsetSql(t);
            int[] types = dataTypeConver.conver(sql.getCloumnType());
            return jdbcTemplate.update(sql.getSql(), sql.getParamsValue().toArray(), types);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> int update(T t) {
        try {
            SQL sql = SqlUtils.buildUpdateSQL(t);
            int[] types = dataTypeConver.conver(sql.getCloumnType());
            return jdbcTemplate.update(sql.getSql(), sql.getParamsValue().toArray(), types);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> int delete(T t) {
        try {
            SQL sql = ClassMethodUtil.getSQL(t);
            return delete(t.getClass(), sql.getPrimaryKeyValue());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static int delete(Class<?> clazz, Object... ids) {
        try {
            String sql = SqlUtils.buildDelteBatchSQL(clazz,ids.length);
            return jdbcTemplate.update(sql, ids);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static <T> T load(final Class<T> clazz, Serializable id) {
        try {
            String sqlStr = SqlUtils.buildQueryByIdSQL(clazz);
            return (T) jdbcTemplate.query(sqlStr, new Serializable[]{id}, new ResultSetExtractor() {
                @Override
                public T extractData(ResultSet rs) throws SQLException, DataAccessException {
                    if (rs.next())
                        return ResultSetUtils.toBean(rs, clazz);
                    return null;
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> List<T> listAll(final Class<T> clazz) {
        try {
            String sql = SqlUtils.buildQueryAllSql(clazz);
            return jdbcTemplate.query(sql, new RowMapper<T>() {
                @Override
                public T mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return ResultSetUtils.toBean(rs, clazz);
                }
            });
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> int[] insertBath(List<T> dataList) {
        if (dataList == null || dataList.size() == 0) {
            return null;
        }
        try {
            SQL sql = SqlUtils.buildInsetSql(dataList.get(0));
            int[] types = dataTypeConver.conver(sql.getCloumnType());
            List<Object[]> prams = SqlUtils.getBathParam(dataList);
            return jdbcTemplate.batchUpdate(sql.getSql(), prams, types);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    public static <T> int[] updateBath(List<T> dataList) {
        if (dataList == null || dataList.size() == 0) {
            return null;
        }
        try {
            SQL sql = SqlUtils.buildUpdateSQL(dataList.get(0));
            int[] types = dataTypeConver.conver(sql.getCloumnType());
            List<Object[]> prams = SqlUtils.getUpdateBathParam(dataList);
            return jdbcTemplate.batchUpdate(sql.getSql(), prams, types);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> int[] deleteBath(List<T> dataList) {
        if (dataList == null || dataList.size() == 0) {
            return null;
        }
        try {
            String sql = SqlUtils.buildDelteSQL(dataList.get(0).getClass());
            List<Object[]> param = SqlUtils.getBathIdsParam(dataList);
            return jdbcTemplate.batchUpdate(sql, param);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static int executeUpdate(String sql, Object... parms) {
        return jdbcTemplate.update(sql, parms);
    }


    public static <T> T get(final Class<T> clazz, String sql, Object... parms) {
        return (T) jdbcTemplate.query(sql, new ResultSetExtractor() {
            @Override
            public T extractData(ResultSet rs) throws SQLException, DataAccessException {
                if (rs.next())
                    return ResultSetUtils.toBean(rs, clazz);
                return null;
            }
        }, parms);
    }

    public static <T> List<T> listByKey(final Class<T> clazz, Object... primaryKey) {
        try {
            String sql = SqlUtils.buildQueryByIdsSQL(clazz, primaryKey.length);
            return jdbcTemplate.query(sql, new RowMapper<T>() {
                @Override
                public T mapRow(ResultSet rs, int rowNum) throws SQLException {
                    return ResultSetUtils.toBean(rs, clazz);
                }
            }, primaryKey);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> List<T> listBySql(final Class<T> clazz, String sql, Object... parms) {
        return jdbcTemplate.query(sql, new RowMapper<T>() {
            @Override
            public T mapRow(ResultSet rs, int rowNum) throws SQLException {
                return ResultSetUtils.toBean(rs, clazz);
            }
        }, parms);
    }

    public static <T> List<T> listBySql(String sql, Object... parms) {
        return jdbcTemplate.query(sql, new RowMapper<T>() {
            @Override
            public T mapRow(ResultSet rs, int rowNum) throws SQLException {
                ResultSetMetaData resultSetMetaData = rs.getMetaData();
                if (resultSetMetaData.getColumnCount() == 1) {
                    return (T) rs.getObject(1);
                } else {
                    return (T) ResultSetUtils.toMap(rs);
                }
            }
        }, parms);
    }

    public static <T> List<List<T>> listList(String sql, Object... parms) {
        return jdbcTemplate.query(sql, new RowMapper<List<T>>() {
            @Override
            public List<T> mapRow(ResultSet rs, int rowNum) throws SQLException {
                List<T> rowList = new ArrayList<T>();
                int count = rs.getMetaData().getColumnCount();
                for (int i = 1; i <= count; i++) {
                    T t = (T) rs.getObject(i);
                    rowList.add(t);
                }
                return rowList;
            }
        }, parms);
    }

    public static List<Map<String, Object>> listMap(String sql, Object... parms) {
        return jdbcTemplate.query(sql, new RowMapper<Map<String, Object>>() {
            @Override
            public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
                return ResultSetUtils.toMap(rs);
            }
        }, parms);
    }


    public static Map<String, Object> readMap(String sql, Object... parms) {
        return jdbcTemplate.query(sql, new RowMapper<Map<String, Object>>() {
            @Override
            public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
                return ResultSetUtils.toMap(rs);
            }
        }, parms).get(0);
    }

    public static boolean exist(String sql, Object... params) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Boolean>() {
            @Override
            public Boolean extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next();
            }
        }, params);
    }

    public static boolean truncate(String tableName) {
        try {
            executeUpdate("TRUNCATE TABLE " + tableName);
            return true;
        } catch (Exception e) {
            return false;
        }

    }

    public static Object getObjValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Object>() {
            @Override
            public Object extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getObject(1) : null;
            }
        }, param);
    }

    public static String getStrValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<String>() {
            @Override
            public String extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getString(1) : null;
            }
        }, param);
    }

    public static Boolean getBooleanValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Boolean>() {
            @Override
            public Boolean extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getBoolean(1) : null;
            }
        }, param);
    }

    public static Date getDateValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Date>() {
            @Override
            public Date extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getDate(1) : null;
            }
        }, param);
    }

    public static Integer getIntValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Integer>() {
            @Override
            public Integer extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getInt(1) : null;
            }
        }, param);
    }

    public static Long getLongValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Long>() {
            @Override
            public Long extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getLong(1) : null;
            }
        }, param);
    }

    public static Double getDoubleValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Double>() {
            @Override
            public Double extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getDouble(1) : null;
            }
        }, param);
    }

    public static Float getFloatValue(String sql, Object... param) {
        return jdbcTemplate.query(sql, new ResultSetExtractor<Float>() {
            @Override
            public Float extractData(ResultSet resultSet) throws SQLException, DataAccessException {
                return resultSet.next() ? resultSet.getFloat(1) : null;
            }
        }, param);
    }

    public static Integer getCount(String sql, Object... param) {
        String countSql = SqlUtils.getCountSQL(sql);
        return getIntValue(countSql, param);
    }

    public static void commit(VoidWrapper voidWrapper) {
        TransactionManager.commit(voidWrapper);
    }


}
